package umg.os.devices.filesystem.logical;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.InputMismatchException;

import umg.os.devices.DataManipulationUtilities;

public class FileControlBlock {

	private final String name;
	private int
	id,
	parentID,
	firstSector;
	private long
	creation,
	edition,
	opening,
	size; 
	
	final static short FILE_CONTROL_BLOCK_SIZE = 254;
	private static SimpleDateFormat dateFormater = new SimpleDateFormat("yyyy/MM/dd - hh:mm:ss.SSS a");
	
	/**
	 * Creates an {@link FileControlBlock} from the <b>data</b>.
	 * 
	 * @param data
	 * The array containing the data of this {@link FileControlBlock}, organized as it follows:
	 * <ol>
	 * <li>
	 * 3 <b>bytes</b> = ID
	 * </li>
	 * <li>
	 * 3 <b>bytes</b> = Parent ID
	 * </li>
	 * <li>
	 * 4 <b>bytes</b> = Creation Date
	 * </li>
	 * <li>
	 * 4 <b>bytes</b> = Edition Date
	 * </li>
	 * <li>
	 * 4 <b>bytes</b> = Opening Date
	 * </li>
	 * <li>
	 * 3 <b>bytes</b> = First Sector
	 * </li>
	 * <li>
	 * 4 <b>bytes</b> = Size
	 * </li>
	 * <li>
	 * 229 <b>bytes</b> = Name
	 * </li>
	 * </ol>
	 */
	public FileControlBlock(byte[] data) {
		if (data.length != FILE_CONTROL_BLOCK_SIZE) {
			throw new InputMismatchException("the data to construct a FileControlBlock MUST be "+FILE_CONTROL_BLOCK_SIZE+" bytes exactly");
		}
		byte[] usableArray = new byte[3];
		System.arraycopy(data, 0, usableArray, 0, 3);
		id = DataManipulationUtilities.intFrom3bytes(usableArray);
		System.arraycopy(data, 3, usableArray, 0, 3);
		parentID = DataManipulationUtilities.intFrom3bytes(usableArray);
		creation = ByteBuffer.wrap(data, 6, 8).getLong();
		edition = ByteBuffer.wrap(data, 14, 8).getLong();
		opening = ByteBuffer.wrap(data, 22, 8).getLong();
		System.arraycopy(data, 30, usableArray, 0, 3);
		firstSector = DataManipulationUtilities.intFrom3bytes(usableArray);
		size = ByteBuffer.wrap(data, 33, 8).getLong();
		//name = new String(ByteBuffer.wrap(data, 41, 213).array());
		usableArray = new byte[213];
		System.arraycopy(data, 41, usableArray, 0, 213);
		name = new String(usableArray).trim();
	}
	
	public FileControlBlock(String name, int parentID) {
		this.name = name;
		this.parentID = parentID;
		creation = edition = opening = System.currentTimeMillis();//TODO: quiza cambiar esto para ser puesto en la fecha de la primera escritura en disco
		id = -1; //An invalid id, until it is created it wont have a real one.
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public int getParentID() {
		return parentID;
	}

	public long getCreation() {
		return creation;
	}

	public long getEdition() {
		return edition;
	}

	public void setEdition(long edition) {
		this.edition = edition;
	}

	public long getOpening() {
		return opening;
	}

	public void setOpening(long opening) {
		this.opening = opening;
	}

	public int getFirstSector() {
		return firstSector;
	}

	public void setFirstSector(int firstSector) {
		if (this.firstSector != 0) {
			throw new UnsupportedOperationException("The first sector of a file cannot be reassigned");
		}
		this.firstSector = firstSector;
	}

	public long getSize() {
		return size;
	}

	public void setSize(long l) {
		this.size = l;
	}

	public String getName() {
		return name;
	}
	
	protected static FileControlBlock generateRootDirectory() {
		FileControlBlock root = new FileControlBlock("root", 0);
		root.id = root.parentID = 0;
		root.opening = root.edition = root.creation = System.currentTimeMillis();
		root.firstSector = 0;
		root.size = 0;
		return root;
	}
	
	public byte[] getDataRepresentation() throws UnsupportedEncodingException {
		byte[] data = new byte[FILE_CONTROL_BLOCK_SIZE],
				idAsBytes = DataManipulationUtilities.intTo3BytesArray(id),
				parentIDAsBytes = DataManipulationUtilities.intTo3BytesArray(parentID),
				creationAsBytes = DataManipulationUtilities.longTo8BytesArray(creation),
				editionAsBytes = DataManipulationUtilities.longTo8BytesArray(edition),
				openingAsBytes = DataManipulationUtilities.longTo8BytesArray(opening),
				firstSectorAsBytes = DataManipulationUtilities.intTo3BytesArray(firstSector),
				sizeAsBytes = DataManipulationUtilities.longTo8BytesArray(size),
				nameAsBytes = new byte[213],
				tempNameBytes = name.getBytes("US-ASCII");
		for (int i = 0; i < 213; i++) {
			if (i < tempNameBytes.length) {
				nameAsBytes[i] = tempNameBytes[i];
			} else {
				nameAsBytes[i] = (byte) 0x00;
			}
		}
		ByteBuffer usableByteBuffer = ByteBuffer.wrap(data);
		usableByteBuffer.put(idAsBytes);
		usableByteBuffer.put(parentIDAsBytes);
		usableByteBuffer.put(creationAsBytes);
		usableByteBuffer.put(editionAsBytes);
		usableByteBuffer.put(openingAsBytes);
		usableByteBuffer.put(firstSectorAsBytes);
		usableByteBuffer.put(sizeAsBytes);
		usableByteBuffer.put(nameAsBytes);
		return data;
	}
	
	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer();
		Calendar c = Calendar.getInstance();
		sb.append("Name: "+name+
				"\nID: "+id+
				"\nParent ID: "+parentID);
		c.setTimeInMillis(creation);
		sb.append("\nCreation Date: "+dateFormater.format(c.getTime()));
		c.setTimeInMillis(edition);
		sb.append("\nEdition Date: "+dateFormater.format(c.getTime()));
		c.setTimeInMillis(opening);
		sb.append("\nOpening Date: "+dateFormater.format(c.getTime())+"\nSize: "+size);
		return sb.toString();
	}
	
}
